package org.jeecg.modules.demo.jxcx.Utils;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.UUID;


public class WXApi {
    private final static char[] hexArray = "0123456789abcdef".toCharArray();

    private static String bytesToHex(byte[] bytes) {
        char[] hexChars = new char[bytes.length * 2];
        for (int j = 0; j < bytes.length; j++) {
            int v = bytes[j] & 0Xff;
            hexChars[j * 2] = hexArray[v >>> 4];
            hexChars[j * 2 + 1] = hexArray[v & 0x0F];
        }
        return new String(hexChars);
    }

    /**
     * 生成签名
     *
     * @param partnerSecret
     * @param partnerId
     * @param timestamp
     * @return
     * @throws UnsupportedEncodingException
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeyException
     */
    private static String createSignature(String partnerSecret, String partnerId, String timestamp)
            throws UnsupportedEncodingException, NoSuchAlgorithmException, InvalidKeyException {
        Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
        SecretKeySpec secret_key = new SecretKeySpec(partnerSecret.getBytes("UTF-8"), "HmacSHA256");
        sha256_HMAC.init(secret_key);
        return bytesToHex(sha256_HMAC.doFinal((partnerId + timestamp).getBytes("UTF-8")));
    }

    /**
     * 发送请求（微信专用）
     *
     * @param path          请求地址
     * @param data          请求数据
     * @param partnerId     合作方id
     * @param partnerSecret 合作方秘钥
     * @return
     * @throws IOException
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeyException
     */
    private static String postData(String path, String data, String partnerId, String partnerSecret)
            throws IOException, NoSuchAlgorithmException, InvalidKeyException {
        HttpURLConnection conn = null;
        URL url = new URL(path);
        conn = (HttpURLConnection) url.openConnection();
        conn.setDoOutput(true);
        conn.setDoInput(true);
        conn.setRequestMethod("POST");
        conn.setUseCaches(false);
        conn.setInstanceFollowRedirects(true);
        conn.setRequestProperty("Content-Type", "application/json");
        conn.setRequestProperty("Accept", "application/json");
        String timestamp = Long.toString(System.currentTimeMillis());
        String signature = createSignature(partnerSecret, partnerId, timestamp);
        String requestId = UUID.randomUUID().toString().replaceAll("-", "");
        conn.setRequestProperty("god-portal-timestamp", timestamp);
        conn.setRequestProperty("god-portal-signature", signature);
        conn.setRequestProperty("god-portal-request-id", requestId);
        conn.connect();
        OutputStream os = conn.getOutputStream();
        os.write(data.getBytes());
        os.flush();
        os.close();
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        InputStream inputStream = conn.getInputStream();
        byte[] bytes = new byte[1024];
        int readBytes;
        while ((readBytes = inputStream.read(bytes)) != -1) {
            byteArrayOutputStream.write(bytes, 0, readBytes);
        }
        bytes = byteArrayOutputStream.toByteArray();
        inputStream.close();
        conn.disconnect();
        return new String(bytes, "utf-8");
    }


    /**
     * 拼接免密授权链接（医保电子凭证）
     *
     * @param cityCode        城市编码：平台分配，如郑州410100
     * @param channel         渠道号（微信医保平台分配），平台分配：平台分配
     * @param orgChnlCrtfCodg 机构渠道认证编码：获取医保线上核验，payAuthNo时为必填字段，使用&orgChnlCrtfCodg=拼接在访问地址上。来源于国家局医保移动支付反馈单。注意需要encode（如果是小程序授权不需要encode）
     * @param orgCodg         定点医药机构编码：获取医保线上核验payAuthNo时必填，使用&orgCodg=拼接在访问地址上。来源于国家局医保移动支付反馈单。
     * @param bizType         线上核验业务类型编码：获取医保线上核验payAuthNo时必填，使用&bizType=04107拼接在访问地址上。医保移动支付业务填04107。
     * @param orgAppId        定点医药机构应用ID：获取医保线上核验payAuthNo时必填，使用&orgAppId=拼接在访问地址上。来源于国家局医保移动支付反馈单。
     * @param envVersion      小程序对应的版本：正式环境: "release"，测试环境: "trial"。
     * @return 1、前端请求示例：
     * wx.navigateToMiniProgram({
     * appId: 'wxe183cd55df4b4369',
     * path:'auth/pages/bindcard/auth/index?openType=getAuthCode&cityCode=CITYCODE&channel=CHANNEL&orgChnlCrtfCodg=XXX&orgCodg=XXX&bizType=04107&orgAppId=XXX ',
     * envVersion: 'trial'
     * })
     * 2、校验成功后回跳接入方小程序，授权码在app.js的onShow函数里通过data.referrerInfo.extraData.authCode获取（注意是在app.js里的onShow()函数）
     * onShow: function (options) {
     * this.globalData.scene = options.scene;
     * if (this.globalData.scene === 1038) {
     * this.globalData.authCode = options.referrerInfo.extraData.authCode;
     * }
     * }
     */
    public static JSONObject authPath(String cityCode, String channel, String orgChnlCrtfCodg, String orgCodg, String bizType, String orgAppId, String envVersion)
            throws Exception {
        //拼接授权链接
        StringBuilder path = new StringBuilder("auth/pages/bindcard/auth/index?openType=getAuthCode");
        path.append("&cityCode=" + cityCode);
        path.append("&channel=" + channel);
        path.append("&orgChnlCrtfCodg=" + orgChnlCrtfCodg);
        path.append("&orgCodg=" + orgCodg);
        path.append("&bizType=" + bizType);
        path.append("&orgAppId=" + orgAppId);
        //封装数据
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("appId", "wxe183cd55df4b4369");//医保移动支付小程序的appid，值为：wxe183cd55df4b4369
        jsonObject.put("path", path.toString());
        jsonObject.put("envVersion", envVersion);
        return jsonObject;
    }

    /**
     * 获取参保人信息
     *
     * @param path          请求地址：
     *                      测试环境地址：https://mip-receiver.tengmed.com/testapi/mipuserquery/userQuery/$partnerId
     *                      正式环境地址：https://mip-receiver.tengmed.com/api/mipuserquery/userQuery/$partnerId
     * @param qrcode        授权码
     * @param openid        患者openid
     * @param partnerId     合作方ID
     * @param partnerSecret 合作方秘钥
     * @return 返回示例：
     * {"code":0,"message":"success","pay_auth_no":"AUTH410100202205241618510007210",
     * "user_longitude_latitude":{"longitude":"113.700057","latitude":"34.747970"}}
     * @throws Exception
     */
    public static JSONObject userQuery(String path, String qrcode, String openid, String partnerId, String partnerSecret)
            throws Exception {
        //组装请求数据
        JSONObject data = new JSONObject();
        data.put("qrcode", qrcode);
        data.put("openid", openid);
        //发送请求，处理返回结果
        String responseStr = postData(path, data.toJSONString(), partnerId, partnerSecret);
        //TODO
        if (StringUtils.isEmpty(responseStr)) {
            throw new Exception("请求失败，未获取到参保人信息!");
        }
        JSONObject result = new JSONObject();
        return result.parseObject(responseStr);
    }
}
